<?php

/*
*	$version: 2009-6-10 20:19:36 beta 1.0
*	$id: ipLocation.class.php 
*	$author: taoFeng
*	$mailto: font99@Gmail.com
*	$license: This is not a freeware
*/

if (!defined('IN_LEKO')) {header("HTTP/1.0 403 Forbidden");exit;}

class ipLocation {

	private $fp;
	private $firstip; //第一条ip索引的偏移地址
	private $lastip;   //最后一条ip索引的偏移地址
	private $totalip; //总ip数
	public $charset;

	function __construct($_d) {
		$this->charset = $GLOBALS['_G']['charSet'];
		$this->fp = fopen($_d,'rb');   //二制方式打开
		$this->firstip = $this->get4b(); //第一条ip索引的绝对偏移地址
		$this->lastip = $this->get4b(); //最后一条ip索引的绝对偏移地址
		$this->totalip = ($this->lastip - $this->firstip) / 7; 
	}

	function __destruct() {
		$this->closeFp();
	}

	public function closeFp() {
		fclose($this->fp);
	}

	/*
	*	读取4个字节并将解压成long的长模式
	*/
	function get4b() {
		$str = unpack("V",fread($this->fp,4));
		return $str[1];
	}

	/*
	*	读取重定向了的偏移地址
	*/
	public function getOffset() {
		$str = unpack("V",fread($this->fp,3).chr(0));
		return $str[1];
	}

	/*
	* 读取ip的详细地址信息
	*/
	public function getIpaddress() {
		$split = fread($this->fp,1);
		$str = '';
		while (ord($split)!=0) {
			$str .= $split;
			$split = fread($this->fp,1);
		}
		if (strcasecmp($this->charset,"utf-8") == 0){
			return iconv("GBK","UTF-8",$str);
		}
		else {
			return $str;
		}
	}

	/*
	* 将ip通过ip2long转成ipv4的互联网地址,再将他压缩成big-endian字节序
	* 用来和索引区内的ip地址做比较
	*/
	public function iptoint($ip){
		return pack("N",intval(ip2long($ip)));
	}

	/*
	* 获取地址信息
	*/
	function readAddress(){
		$now_offset = ftell($this->fp); //得到当前的指针位址
		$flag = $this->getFlag();
		$address = "";
		switch (ord($flag)) {
			case 0:
				break;
		   case 1:
		   case 2:
				fseek($this->fp,$this->getOffset());
				$address = $this->getIpaddress();
		   break;
		   default:
				fseek($this->fp,$now_offset);
				$address = $this->getIpaddress();
		   break;
		}

		if (strcasecmp($this->charset,"utf-8") == 0){
			return iconv("GBK","UTF-8",$address);
		}
		else {
			return $address;
		}
	}

	/*
	* 获取标志1或2
	* 用来确定地址是否重定向了.
	*/
	function getFlag(){
		return fread($this->fp,1);
	}

	/*
	* 用二分查找法在索引区内搜索ip
	*/
	public function searchIp($ip){
		$c = preg_match("/(\d.)+/",$ip,$match);
		if (!$c)
			$ip = gethostbyname($ip);     //将域名转成ip
		$ip_offset["ip"]=$ip;
		$ip=$this->iptoint($ip);    //将ip转换成长整型
		$firstip=0;                 //搜索的上边界
		$lastip=$this->totalip;     //搜索的下边界
		$ipoffset=$this->lastip;    //初始化为最后一条ip地址的偏移地址
		while ($firstip <= $lastip) {
			$i = floor(($firstip + $lastip) / 2);          //计算近似中间记录 floor函数记算给定浮点数小的最大整数,说白了就是四舍五也舍
			fseek($this->fp,$this->firstip + $i * 7);    //定位指针到中间记录
			$startip = strrev(fread($this->fp,4));         //读取当前索引区内的开始ip地址,并将其little-endian的字节序转换成big-endian的字节序
			if ($ip < $startip) {
				$lastip = $i - 1;
			}
			else {
				fseek($this->fp,$this->getOffset());
				$endip=strrev(fread($this->fp,4));
				if ($ip > $endip){
				   $firstip=$i + 1;
				}
				else {
				   $ip_offset["offset"]=$this->firstip + $i * 7;
				   break;
				}
			}
		}
		return $ip_offset;
	}

	/*
	 获取ip地址详细信息
	*/
	public function getAddress($ip) {
		$ip_offset=$this->searchIp($ip); //获取ip 在索引区内的绝对编移地址
		$ipoffset=$ip_offset["offset"];
		$address["ip"]=$ip_offset["ip"];
		fseek($this->fp,$ipoffset);      //定位到索引区
		$address["startip"]=long2ip($this->get4b()); //索引区内的开始ip 地址
		$address_offset=$this->getOffset();            //获取索引区内ip在ip记录区内的偏移地址
		fseek($this->fp,$address_offset);            //定位到记录区内
		$address["endip"]=long2ip($this->get4b());   //记录区内的结束ip 地址
		$flag=$this->getFlag();                      //读取标志字节
		switch (ord($flag)) {
			case 1: //地区1地区2都重定向
			$address_offset=$this->getOffset();   //读取重定向地址
			fseek($this->fp,$address_offset);     //定位指针到重定向的地址
			$flag=$this->getFlag();               //读取标志字节
			switch (ord($flag)) {
				case 2: //地区1又一次重定向,
				fseek($this->fp,$this->getOffset());
				$address["area1"]=$this->getIpaddress();
				fseek($this->fp,$address_offset+4);      //跳4个字节
				$address["area2"]=$this->readAddress(); //地区2有可能重定向,有可能没有
				break;
				default: //地区1,地区2都没有重定向
				fseek($this->fp,$address_offset);        //定位指针到重定向的地址
				$address["area1"]=$this->getIpaddress();
				$address["area2"]=$this->readAddress();
				break;
		   }
		   break;
		   case 2: //地区1重定向 地区2没有重定向
			   $address1_offset=$this->getOffset();   //读取重定向地址
			   fseek($this->fp,$address1_offset); 
			   $address["area1"]=$this->getIpaddress();
			   fseek($this->fp,$address_offset+8);
			   $address["area2"]=$this->readAddress();
			   break;
		   default: //地区1地区2都没有重定向
			   fseek($this->fp,$address_offset+4);
			   $address["area1"]=$this->getIpaddress();
			   $address["area2"]=$this->readAddress();
		   break;
		}
		//*过滤一些无用数据
		if (strpos($address["area1"],"CZ88.NET")!=false){
			  $address["area1"]="UNKNOW";
		}
		if (strpos($address["area2"],"CZ88.NET")!=false){
			  $address["area2"]="UNKNOW";
		}
		return $address;
	}
} 

?>